/*
  Copyright (c) 2010
  The Regents of the University of Michigan
  All Rights Reserved

  Permission is granted to use, copy, create derivative works, and
  redistribute this software and such derivative works for any purpose,
  so long as the name of the University of Michigan is not used in
  any advertising or publicity pertaining to the use or distribution
  of this software without specific, written prior authorization. If
  the above copyright notice or any other identification of the
  University of Michigan is included in any copy of any portion of
  this software, then the disclaimer below must also be included.

  This software is provided as is, without representation or warranty
  of any kind either express or implied, including without limitation
  the implied warranties of merchantability, fitness for a particular
  purpose, or noninfringement.  The Regents of the University of
  Michigan shall not be liable for any damages, including special,
  indirect, incidental, or consequential damages, with respect to any
  claim arising out of or in connection with the use of the software,
  even if it has been or is hereafter advised of the possibility of
  such damages.
*/
#include "handlers.h"

#include "clearview_internal.h"
#include "lookup.h"
#include "id.h"
#include "network.h"
#include "utils.h"

#include <stdio.h>
#include <stdlib.h>

#include <glib.h>

extern struct in_addr local_addr;

void handle_ping_request(struct RequestPingPacket *req,
                         struct ClearviewInstance *cv,
                         struct ContactAddress *contact) {
#ifdef VERBOSE_LOG
  puts("Received ping request!");
#endif

  // Ignore send failure.
  clearview_send_ping_reply(cv, &req->header);
}


void handle_store_request(struct RequestStorePacket *req,
                          struct ClearviewInstance *cv,
                          struct ContactAddress *contact) {
#ifdef VERBOSE_LOG
  fprintf(stderr, "******************************************\n");
  fprintf(stderr, "Received store request with %d keys&%d value groups from"
                  "%s:%hu\n",
         (int)req->keys_count, (int)req->value_groups_count,
         inet_ntoa(req->header.node_address.addr),
         req->header.node_address.port);
#endif

  logger_log_store(&cv->log, req);
  // Ignore send failure.
  clearview_send_empty_store_reply(cv, &req->header);
  store_request_free_data(req);
}


void handle_find_node_request(struct RequestFindNodePacket *req,
                              struct ClearviewInstance *cv,
                              struct ContactAddress *contact) {
#ifdef VERBOSE_LOG
  fprintf(stderr, "Received find node request from %s:%hu\n",
          inet_ntoa(req->header.node_address.addr),
          req->header.node_address.port);
#endif

  if (clearview_id_shared_prefix_bits(
          (unsigned char *)cv->id,
          (unsigned char *)clearview_get_id(contact)) >= 8) {
    clearview_send_unhelpful_find_node_reply(cv, &req->header);
  } else {
#ifdef VERBOSE_LOG
    fputs("Rejected find-node request.\n", stderr);
#endif
  }

  // DO need to free this ID!
  clearview_id_free(contact->id);
}


void handle_find_value_request(struct RequestFindValuePacket *req,
                               struct ClearviewInstance *cv,
                               struct ContactAddress *contact) {
#ifdef VERBOSE_LOG
  fprintf(stderr, "Received find value request from %s:%hu\n",
          inet_ntoa(req->header.node_address.addr),
          req->header.node_address.port);
#endif

  /* New strategy: ignore incoming find-value requests. */
  return;
}


void handle_error_reply(struct ReplyErrorPacket *reply,
                        struct ClearviewInstance *cv,
                        struct ContactAddress *contact) {
  fprintf(stderr, "Error packet to port %hu from %s:%hu: ",
          cv->port, inet_ntoa(contact->addr), contact->port);
  if (reply->error_type == WRONG_ADDRESS) {
    fprintf(stderr, "Wrong sender address; should be %s:%hu\n",
            inet_ntoa(reply->sender_address.addr), reply->sender_address.port);
  } else {
    fprintf(stderr, "key blocked, ignoring\n");
  }
}


void handle_ping_reply(struct ReplyPingPacket *reply,
                       struct ClearviewInstance *cv,
                       struct ContactAddress *contact) {
#ifdef VERBOSE_LOG
  fprintf(stderr, "Received ping reply from %s:%hu\n",
          inet_ntoa(contact->addr), contact->port);
#endif
}


#if 0
struct MinData {
  gpointer cur_min;
  GCompareDataFunc cmp;
  gpointer cmp_data;
};

static void g_sequence_min_helper(gpointer elt, gpointer data) {
  struct MinData *my_data = data;

  if (my_data->cmp(elt, my_data->cur_min, my_data->cmp_data) < 0)  {
    my_data->cur_min = elt;
  }
}

static gpointer g_sequence_min(GSequence *seq, GCompareDataFunc cmp,
                               gpointer cmp_data) {
  struct MinData data = {g_sequence_get(g_sequence_get_begin_iter(seq)),
                         cmp,
                         cmp_data};
  g_sequence_foreach(seq, g_sequence_min_helper, &data);
  return data.cur_min;
}
#endif


void handle_find_node_reply(struct ReplyFindNodePacket *reply,
                            struct ClearviewInstance *cv,
                            struct ContactAddress *contact) {
#if defined(VERBOSE_LOG) || defined(LOG_FIND_REPLIES)
  printf("Received find node reply from %s:%hu, %d contacts:\n",
         inet_ntoa(contact->addr), contact->port, reply->contacts_count);

  int ii;
  for (ii = 0; ii != reply->contacts_count; ++ii) {
    printf("Contact: %s:%hu\n", inet_ntoa(reply->contacts[ii]->addr),
           reply->contacts[ii]->port);
  }
#endif

  // We only make find_node requests during a lookup. On with the lookup!

  // TODO(swolchok): this code still assumes there can only be one lookup and
  // it's the bootstrap. Need to change when we go to true multi-lookup.
  if (cv->lookups && cv->nlookups) {
    struct Lookup *lookup = g_ptr_array_index(cv->lookups, 0);

    g_return_if_fail(lookup != NULL);
    // TODO(swolchok): when we move to multi-lookup, memory management needs to
    // change -- either free the contacts after this or refcount them and
    // remember to decrement the count.
    lookup_handle_find_reply(lookup, reply, contact);
  }
}


